package com.cq.hd.member.factory;

import com.cq.hd.common.constant.RedisKeyConstant;
import com.cq.hd.common.enums.biz.*;
import com.cq.hd.common.response.Throw;
import com.cq.hd.common.utils.*;
import com.cq.hd.member.api.dto.WithdrawalApplyDto;
import com.cq.hd.member.api.dto.WithdrawalApplyV2Dto;
import com.cq.hd.member.api.vo.BusinessEnterApplyDetailVo;
import com.cq.hd.member.api.vo.MerchantDetailVo;
import com.cq.hd.member.api.vo.MerchantEnterApplyVo;
import com.cq.hd.member.mq.provider.WithdrawalMsgProvider;
import com.cq.hd.member.po.TbMerchantAmtBillPo;
import com.cq.hd.member.po.TbWithdrawalBillPo;
import com.cq.hd.member.service.*;
import com.cq.hd.order.api.TbOrderSettleBillApi;
import com.cq.hd.order.api.dto.OrderSettleBillDto;
import com.cq.hd.order.api.vo.MonthSettleBillVo;
import com.cq.hd.order.api.vo.OrderSettleBillVo;
import com.cq.hd.system.api.vo.WithdrawalConfigVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Component
@Slf4j
public class MerchantAmtWithdrawalHandler extends BaseOnMonthSettleWithdrawalHandler {

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private TbMerchantService merchantService;

    @Autowired
    private TbMerchantEnterApplyService merchantEnterApplyService;

    @Autowired
    private TbBusinessEnterApplyService businessEnterApplyService;

    @Autowired
    private TbWithdrawalBillService withdrawalBillService;

    @Autowired
    private TbMerchantAmtBillService merchantAmtBillService;

    @Autowired
    private WithdrawalMsgProvider withdrawalMsgProvider;

    @Autowired
    private TbOrderSettleBillApi orderSettleBillApi;

    @Autowired
    private PlatformTransactionManager transactionManager;

    @Override
    protected Integer getType() {
        return WithdrawalTypeEnum.MERCHANT_AMT.getValue();
    }

    @Override
    public Boolean apply(WithdrawalApplyDto withdrawalApplyDto, WithdrawalConfigVo withdrawalConfigVo) {
        // 如果是商户本金/商户补贴提现，就是商户id，如果是佣金提现，则是用户id
        Long userId = withdrawalApplyDto.getUserId();
        BigDecimal tradeAmt = withdrawalApplyDto.getTradeAmt();

        MerchantDetailVo merchantDetailVo = merchantService.getDetailById(userId);
        if (merchantDetailVo == null) {
            Throw.isBusinessException("请先成为商户");
        }
        BigDecimal balanceAmt = merchantDetailVo.getBalanceAmt();

        if (tradeAmt.compareTo(balanceAmt) > 0) {
            Throw.isBusinessException("商户本金余额不足");
        }

        String openid = merchantDetailVo.getOpenid();
        if (StringUtils.isBlank(openid)) {
            log.error("申请商户本金提现失败，未找到商户的openid，merchantId：{}", userId);
            Throw.isBusinessException("商户数据错误");
        }

        String hfId = merchantDetailVo.getHfMerchantNo();
        String acctId = "";
        String tokenNo = merchantDetailVo.getWithdrawalCardNo();

        String cardName = "";
        String cardNo = "";
        String bankName = "";
        MerchantEnterApplyVo enterInfoByOpenid = merchantEnterApplyService.getSuccessEnterInfoByOpenid(openid);
        if (enterInfoByOpenid == null) {
            // 表示自有商户申请提现，查找商家入驻数据
            BusinessEnterApplyDetailVo businessEnterApplyDetailVo = businessEnterApplyService.getSuccessEnterInfoByOpenid(openid);
            if (businessEnterApplyDetailVo == null) {
                log.error("申请商户本金提现失败，未找到商家/商户入驻的数据，merchantId：{}", userId);
                Throw.isBusinessException("商户数据错误");
            }

            hfId = businessEnterApplyDetailVo.getHfMerchantNo();
            acctId = businessEnterApplyDetailVo.getHfAcctId();
            tokenNo = businessEnterApplyDetailVo.getWithdrawalCardNo();
            cardName = businessEnterApplyDetailVo.getCardName();
            cardNo = businessEnterApplyDetailVo.getCardNo();
        } else {
            String bankCode = enterInfoByOpenid.getBankCode();
            if (!StringUtils.isBlank(bankCode)) {
                HfBankIdEnum hfBankIdEnum = HfBankIdEnum.typeOfValue(bankCode);
                if (hfBankIdEnum != null) {
                    bankName = hfBankIdEnum.getName();
                }
            }

            cardName = enterInfoByOpenid.getCardName();
            cardNo = enterInfoByOpenid.getCardNo();
        }

        if (StringUtils.isBlank(cardName) || StringUtils.isBlank(cardNo)) {
            log.error("申请商户本金提现失败，未找到商户的银行卡相关数据，merchantId：{}", userId);
            Throw.isBusinessException("商户数据错误");
        }

        // 起提金额
        BigDecimal minAmt = withdrawalConfigVo.getMinAmt();
        // 单次最高可提金额
        BigDecimal singleMaxAmt = withdrawalConfigVo.getSingleMaxAmt();
        // 单日可提次数
        Integer dayCount = withdrawalConfigVo.getDayCount();
        // 提现自动审核方式(1-人工审核，2-自动审核)
        Integer autoApprovalType = withdrawalConfigVo.getAutoApprovalType();
        // 自动审核提现金额
        BigDecimal autoApprovalAmt = withdrawalConfigVo.getAutoApprovalAmt();

        // 今日提现次数已达上限
        if (tradeAmt.compareTo(minAmt) < 0) {
            Throw.isBusinessException("最少" + minAmt + "元起提");
        }

        // 最高可提xxx元
        if (tradeAmt.compareTo(singleMaxAmt) > 0) {
            Throw.isBusinessException("最高可提" + singleMaxAmt + "元");
        }

        LocalDateTime nowLocalDateTime = LocalDateUtil.getNowLocalDateTime();
        String yyyyMMdd = LocalDateUtil.toLocalDateToString(nowLocalDateTime.toLocalDate(), "yyyyMMdd");
        String reqSeqId = LocalDateUtil.toLocalDateTimeToString(nowLocalDateTime, "yyyyMMddHHmmss") + GenerateUtil.getRandomNum(5);

        // 今日提现次数已达上限
        String countStr = redisUtil.get(String.format(RedisKeyConstant.WITHDRAWAL_DAY_NUM_KEY, withdrawalApplyDto.getWithdrawalType(), yyyyMMdd, userId));
        if (!StringUtils.isBlank(countStr)) {
            int count = Integer.parseInt(countStr);
            if (count >= dayCount) {
                Throw.isBusinessException("今日提现次数已达上限");
            }
        }

        // 需要对待支付的结算资金做查单处理，如果存在待支付的结算资金，则需要再次做余额支付税筹，如果转失败则修改状态
        OrderSettleBillDto orderSettleBillDto = new OrderSettleBillDto();
        orderSettleBillDto.setMerchantId(userId);
        orderSettleBillDto.setTradeType(OrderSettleBillTradeTypeEnum.MERCHANT_AMT.getValue());
        orderSettleBillDto.setTradeStates(Arrays.asList(OrderSettleBillTradeStateEnum.WAIT.getValue(), OrderSettleBillTradeStateEnum.FAIL.getValue()));
        List<OrderSettleBillVo> orderSettleBillVos = orderSettleBillApi.listByDto(orderSettleBillDto).unpack();
        if (!CollectionUtils.isEmpty(orderSettleBillVos)) {
            for (OrderSettleBillVo orderSettleBillVo : orderSettleBillVos) {
                orderSettleBillApi.transactAmt(orderSettleBillVo);
            }
        }

        // 计算服务费
//        // 需要自行计算手续费,由商户承担, 消费者下单，平台到账金额会扣除0.3%手续费+0.2%平台抽取，最低收取0.01元，这块由商户承担，余额支付(给商户分本金)接口不会扣除手续费
//        BigDecimal feeAmt = tradeAmt.multiply(Constant.MERCHANT_AMT_RATE).setScale(2, BigDecimal.ROUND_HALF_UP);
//        // 不足0.01,按0.01扣
//        if (feeAmt.compareTo(new BigDecimal("0.01")) < 0) {
//            feeAmt = new BigDecimal("0.01");
//        }
//        BigDecimal calReceiveAmt = tradeAmt.subtract(feeAmt);

        // 2024-04-09 服务费改成支付接口扣除
        BigDecimal feeAmt = BigDecimal.ZERO;
        BigDecimal calReceiveAmt = tradeAmt;

        TbWithdrawalBillPo tbWithdrawalBillPo = new TbWithdrawalBillPo();
        tbWithdrawalBillPo.setApplyNo(NoUtils.getWithdrawalBillNo());
        tbWithdrawalBillPo.setWithdrawalUserId(userId);
        tbWithdrawalBillPo.setBusinessId(0L);
        tbWithdrawalBillPo.setWithdrawalType(WithdrawalTypeEnum.MERCHANT_AMT.getValue());
        tbWithdrawalBillPo.setCardName(cardName);
        tbWithdrawalBillPo.setCardNo(cardNo);
        tbWithdrawalBillPo.setBankName(bankName);
        tbWithdrawalBillPo.setBeforeAmt(balanceAmt);
        tbWithdrawalBillPo.setTradeAmt(tradeAmt);
        tbWithdrawalBillPo.setAfterAmt(balanceAmt.subtract(tradeAmt));
        tbWithdrawalBillPo.setFeeAmt(feeAmt);
        tbWithdrawalBillPo.setReceiveAmt(calReceiveAmt);
        tbWithdrawalBillPo.setApplyTime(nowLocalDateTime);
        tbWithdrawalBillPo.setReqDate(yyyyMMdd);
        tbWithdrawalBillPo.setReqSeqId(reqSeqId);
        tbWithdrawalBillPo.setHfId(hfId);
        tbWithdrawalBillPo.setAcctId(acctId);
        tbWithdrawalBillPo.setTokenNo(tokenNo);
        tbWithdrawalBillPo.setCreateTime(nowLocalDateTime);
        tbWithdrawalBillPo.setUpdateTime(nowLocalDateTime);

        Long id = 0L;
        TransactionStatus transaction = transactionManager.getTransaction(new DefaultTransactionAttribute());

        try {
            // 扣除商户本金账户资金
            int subMerchantAmtCount = merchantService.subMerchantAmt(userId, tradeAmt);
            if (subMerchantAmtCount == 0) {
                Throw.isBusinessException("商户本金账户余额不足");
            }

            // 自动审核，判断申请提现金额是否小于等于自动审核提现金额
            if (autoApprovalType != null && autoApprovalType == 2
                    && autoApprovalAmt != null && autoApprovalAmt.compareTo(BigDecimal.ZERO) > 0
                    && tradeAmt.compareTo(autoApprovalAmt) <= 0) {
                // 走自动提现

                tbWithdrawalBillPo.setState(WithdrawalStateEnum.AUDIT_SUCCESS.getValue());
                boolean save = withdrawalBillService.save(tbWithdrawalBillPo);
                if (!save) {
                    Throw.isBusinessException("申请提现失败");
                }

                id = tbWithdrawalBillPo.getId();
            } else {
                // 走人工审核
                tbWithdrawalBillPo.setState(WithdrawalStateEnum.WAIT_AUDIT.getValue());
                boolean save = withdrawalBillService.save(tbWithdrawalBillPo);
                if (!save) {
                    Throw.isBusinessException("申请提现失败");
                }
            }

            // 增加商户资金流水记录
            TbMerchantAmtBillPo tbMerchantAmtBillPo = new TbMerchantAmtBillPo();
            tbMerchantAmtBillPo.setBillNo(NoUtils.getMerchantAmtBillNo());
            tbMerchantAmtBillPo.setMerchantId(userId);
            tbMerchantAmtBillPo.setBillType(BillTypeEnum.EXPEND.getValue());
            tbMerchantAmtBillPo.setTradeType(MemberAmtBillTradeTypeEnum.WITHDRAWAL_AMT.getValue());
            tbMerchantAmtBillPo.setRelationTradeNo(tbWithdrawalBillPo.getApplyNo());
            tbMerchantAmtBillPo.setBeforeAmt(tbWithdrawalBillPo.getBeforeAmt());
            tbMerchantAmtBillPo.setTradeAmt(tbWithdrawalBillPo.getTradeAmt());
            tbMerchantAmtBillPo.setAfterAmt(tbWithdrawalBillPo.getAfterAmt());
            tbMerchantAmtBillPo.setTradeTime(tbWithdrawalBillPo.getApplyTime());
            tbMerchantAmtBillPo.setCreateTime(LocalDateTime.now());
            tbMerchantAmtBillPo.setUpdateTime(LocalDateTime.now());
            boolean save = merchantAmtBillService.save(tbMerchantAmtBillPo);
            if (!save) {
                Throw.isBusinessException("申请提现失败");
            }

            transactionManager.commit(transaction);
        } catch (Exception e) {
            //回滚事务
            transactionManager.rollback(transaction);
            log.error("<申请商户本金提现>事务异常回滚", e);
            Throw.isBusinessException(e.getMessage());
        }

        if (id > 0) {
            // 发送mq消息做异步提现打款
            withdrawalMsgProvider.sendMsg(id);
        }

        // 增加提现次数
        redisUtil.incr(String.format(RedisKeyConstant.WITHDRAWAL_DAY_NUM_KEY, withdrawalApplyDto.getWithdrawalType(), yyyyMMdd, userId));

        return true;
    }

    @Override
    public Boolean applyV2(WithdrawalApplyV2Dto withdrawalApplyV2Dto, WithdrawalConfigVo withdrawalConfigVo) {
        // 初始化月度账单ids
        Long merchantId = withdrawalApplyV2Dto.getUserId();
        List<MonthSettleBillVo> monthSettleBillVos = monthSettleBillApi.getMerchantWaitMonthSettleBill(
                merchantId, MonthSettleBillTypeEnum.MERCHANT_AMT.getValue()).unpack();
        if (!CollectionUtils.isEmpty(monthSettleBillVos)) {
            List<Long> monthSettleIds = monthSettleBillVos.stream().map(MonthSettleBillVo::getId).collect(Collectors.toList());
            withdrawalApplyV2Dto.setMonthSettleBillIds(monthSettleIds);
        }

        return super.applyV2(withdrawalApplyV2Dto, withdrawalConfigVo);
    }

    @Override
    protected BigDecimal getMaxWithdrawalAmt(MerchantDetailVo merchantDetailVo) {
        return Optional.ofNullable(merchantDetailVo.getBalanceAmt()).orElse(BigDecimal.ZERO);
    }

    @Override
    protected BigDecimal getWithdrawalAmount(WithdrawalContext withdrawalContext) {
        BigDecimal applyAmt = withdrawalContext.getWithdrawalApplyV2Dto().getApplyAmt();
        BigDecimal reduce = withdrawalContext.getMonthSettleBills().stream()
                .map(MonthSettleBillVo::getTradeAmt)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        if (applyAmt.compareTo(reduce) != 0) {
            log.error("[结算池商户提现] - 提现金额与账单不一致");
            Throw.isBusinessException("系统错误，请稍后重试");
        }
        return applyAmt;
    }

    @Override
    protected Integer getWithdrawalType() {
        return WithdrawalTypeEnum.MERCHANT_AMT.getValue();
    }

    @Override
    protected int updateMerchantAccountAmt(WithdrawalContext withdrawalContext) {
        return merchantService.subMerchantAmt(withdrawalContext.getMerchantDetailVo().getId(), withdrawalContext.getWithdrawalAmt());
    }

}
